<template>
	<div>
		<el-dialog v-dialogDrag top="5vh" custom-class="rtc-private-video-dialog" :title="title" :width="width"
			:visible.sync="showRoom" :close-on-click-modal="false" :close-on-press-escape="false"
			:before-close="onQuit">
			<div class="rtc-private-video">
				<div v-show="isVideo" class="rtc-video-box">
					<div class="rtc-video-friend" v-loading="!isChating" element-loading-text="等待对方接听..."
						element-loading-background="rgba(0, 0, 0, 0.1)">
						<head-image class="friend-head-image" :id="friend.id" :size="80" :name="friend.nickName"
							:url="friend.headImage" :isShowUserInfo="false" radius="0">
						</head-image>
						<video ref="remoteVideo" autoplay=""></video>
					</div>
					<div class="rtc-video-mine">
						<video ref="localVideo" autoplay=""></video>
					</div>
				</div>
				<div v-show="!isVideo" class="rtc-voice-box" v-loading="!isChating" element-loading-text="等待对方接听..."
					element-loading-background="rgba(0, 0, 0, 0.1)">
					<head-image class="friend-head-image" :id="friend.id" :size="200" :name="friend.nickName"
						:url="friend.headImage" :isShowUserInfo="false">
						<div class="rtc-voice-name">{{ friend.nickName }}</div>
					</head-image>
				</div>
				<div class="rtc-control-bar">
					<div title="取消" class="icon iconfont icon-phone-reject reject" style="color: red;"
						@click="onQuit()"></div>
				</div>
			</div>
		</el-dialog>
		<rtc-private-acceptor v-if="!isHost && isWaiting" ref="acceptor" :friend="friend" :mode="mode"
			@accept="onAccept" @reject="onReject"></rtc-private-acceptor>
	</div>
</template>

<script>
import HeadImage from '../common/HeadImage.vue';
import RtcPrivateAcceptor from './RtcPrivateAcceptor.vue';
import ImWebRtc from '@/api/webrtc';
import ImCamera from '@/api/camera';
import RtcPrivateApi from '@/api/rtcPrivateApi'

export default {
	name: 'rtcPrivateVideo',
	components: {
		HeadImage,
		RtcPrivateAcceptor
	},
	data() {
		return {
			camera: new ImCamera(), // 摄像头和麦克风
			webrtc: new ImWebRtc(), // webrtc相关
			API: new RtcPrivateApi(), // API
			audio: new Audio(), // 呼叫音频
			showRoom: false,
			friend: {},
			isHost: false, // 是否发起人
			state: "CLOSE", // CLOSE:关闭  WAITING:等待呼叫或接听 CHATING:聊天中  ERROR:出现异常
			mode: 'video', // 模式 video:视频聊 voice:语音聊天
			localStream: null, // 本地视频流
			remoteStream: null, // 对方视频流
			videoTime: 0,
			videoTimer: null,
			heartbeatTimer: null,
			candidates: [],
		}
	},
	methods: {
		open(rtcInfo) {
			this.showRoom = true;
			this.mode = rtcInfo.mode;
			this.isHost = rtcInfo.isHost;
			this.friend = rtcInfo.friend;
			if (this.isHost) {
				this.onCall();
			}
		},
		initAudio() {
			let url = require(`@/assets/audio/call.wav`);
			this.audio.src = url;
			this.audio.loop = true;
		},
		initRtc() {
			this.webrtc.init(this.configuration)
			this.webrtc.setupPeerConnection((stream) => {
				this.$refs.remoteVideo.srcObject = stream;
				this.remoteStream = stream;
			})
			// 监听候选信息
			this.webrtc.onIcecandidate((candidate) => {
				if (this.state == "CHATING") {
					// 连接已就绪,直接发送
					this.API.sendCandidate(this.friend.id, candidate);
				} else {
					// 连接未就绪,缓存起来，连接后再发送
					this.candidates.push(candidate)
				}
			})
			// 监听连接成功状态
			this.webrtc.onStateChange((state) => {
				if (state == "connected") {
					console.log("webrtc连接成功")
				} else if (state == "disconnected") {
					console.log("webrtc连接断开")
				}
			})
		},
		onCall() {
			if (!this.checkDevEnable()) {
				this.close();
			}
			// 初始化webrtc
			this.initRtc();
			// 启动心跳
			this.startHeartBeat();
			// 打开摄像头
			this.openStream().then(() => {
				this.webrtc.setStream(this.localStream);
				// 发起呼叫
				this.API.setup(this.friend.id, this.mode).then(() => {
					// 进入等待状态
					this.state = "WAITING";
					// 播放呼叫铃声
					this.audio.play();
				}).catch(() => {
					this.close();
				})
			}).catch(() => {
				// 呼叫方必须能打开摄像头，否则无法正常建立连接
				this.close();
			})
		},
		onAccept() {
			if (!this.checkDevEnable()) {
				this.API.failed(this.friend.id, "对方设备不支持通话")
				this.close();
				return;
			}
			// 进入房间
			this.showRoom = true;
			// 停止呼叫铃声
			this.audio.pause();
			// 初始化webrtc
			this.initRtc();
			// 打开摄像头
			this.openStream().finally(() => {
				this.webrtc.setStream(this.localStream);
				this.API.accept(this.friend.id);
			})
		},
		onReject() {
			console.log("onReject")
			// 退出通话
			this.API.reject(this.friend.id);
			// 退出
			this.close();
		},
		onHandup() {
			this.API.handup(this.friend.id)
			this.$message.success("您已挂断,通话结束")
			this.close();
		},
		onCancel() {
			this.API.cancel(this.friend.id)
			this.$message.success("已取消呼叫,通话结束")
			this.close();
		},
		onRTCMessage(msg) {
			// 除了发起通话，如果在关闭状态就无需处理
			if (msg.type != this.$enums.MESSAGE_TYPE.RTC_SETUP_VOICE &&
				msg.type != this.$enums.MESSAGE_TYPE.RTC_SETUP_VIDEO &&
				this.isClose) {
				return;
			}
			// RTC信令处理
			switch (msg.type) {
				case this.$enums.MESSAGE_TYPE.RTC_SETUP_VOICE:
					this.onRTCSetup(msg, 'voice')
					break;
				case this.$enums.MESSAGE_TYPE.RTC_SETUP_VIDEO:
					this.onRTCSetup(msg, 'video')
					break;
				case this.$enums.MESSAGE_TYPE.RTC_ACCEPT:
					this.onRTCAccept(msg)
					break;
				case this.$enums.MESSAGE_TYPE.RTC_REJECT:
					this.onRTCReject(msg)
					break;
				case this.$enums.MESSAGE_TYPE.RTC_CANCEL:
					this.onRTCCancel(msg)
					break;
				case this.$enums.MESSAGE_TYPE.RTC_FAILED:
					this.onRTCFailed(msg)
					break;
				case this.$enums.MESSAGE_TYPE.RTC_HANDUP:
					this.onRTCHandup(msg)
					break;
				case this.$enums.MESSAGE_TYPE.RTC_OFFER:
					this.onRTCOffer(msg)
					break;
				case this.$enums.MESSAGE_TYPE.RTC_ANSWER:
					this.onRTCAnswer(msg)
					break;
				case this.$enums.MESSAGE_TYPE.RTC_CANDIDATE:
					this.onRTCCandidate(msg)
					break;
			}
		},
		onRTCSetup(msg, mode) {
			this.isHost = false;
			this.mode = mode;
			this.$http({
				url: `/friend/find/${msg.sendId}`,
				method: 'get'
			}).then((friend) => {
				this.friend = friend;
				this.state = "WAITING";
				this.audio.play();
				this.startHeartBeat();
			})
		},
		onRTCAccept(msg) {
			if (msg.selfSend) {
				// 在其他设备接听
				this.$message.success("已在其他设备接听");
				this.close();
			} else {
				// 对方接受了的通话
				this.webrtc.createOffer().then((offer) => {
					// 停止播放语音
					this.audio.pause();
					// 推送offer给对方
					this.API.offer(this.friend.id, offer);
				})
			}
		},
		onRTCReject(msg) {
			if (msg.selfSend) {
				this.$message.success("已在其他设备拒绝");
				this.close();
			} else {
				this.$message.error("对方拒绝了您的通话请求");
				this.close();
			}
		},
		onRTCFailed(msg) {
			// 呼叫失败
			if (msg.selfSend) {
				this.$message.error("您未接听")
				this.close();
			} else {
				this.$message.error(msg.content)
				this.close();
			}

		},
		onRTCCancel() {
			// 对方取消通话
			this.$message.success("对方取消了呼叫");
			this.close();
		},
		onRTCHandup() {
			// 对方挂断
			this.$message.success("对方已挂断");
			this.close();
		},
		onRTCOffer(msg) {
			const offer = JSON.parse(msg.content);
			console.log(offer)
			this.webrtc.createAnswer(offer).then((answer) => {
				// 推送answer给对方
				this.API.answer(this.friend.id, answer);
			})
			// 状态为聊天中
			this.state = 'CHATING';
			// 记录时长
			this.startChatTime();
		},
		onRTCAnswer(msg) {
			const answer = JSON.parse(msg.content);
			this.webrtc.setRemoteDescription(answer);
			// 发送candidate
			this.candidates.forEach((candidate) => {
				this.API.sendCandidate(this.friend.id, candidate);
			})
			// 状态为聊天中
			this.state = 'CHATING';
			// 记录时长
			this.startChatTime();
		},
		onRTCCandidate(msg) {
			let candidate = JSON.parse(msg.content);
			this.webrtc.addIceCandidate(candidate);
		},

		openStream() {
			return new Promise((resolve, reject) => {
				if (this.isVideo) {
					// 打开摄像头+麦克风
					this.camera.openVideo().then((stream) => {
						this.localStream = stream;
						this.$nextTick(() => {
							this.$refs.localVideo.srcObject = stream;
							this.$refs.localVideo.muted = true;
						})
						resolve(stream);
					}).catch((e) => {
						this.$message.error("打开摄像头失败")
						console.log("本摄像头打开失败:" + e.message)
						reject(e);
					})
				} else {
					// 打开麦克风
					this.camera.openAudio().then((stream) => {
						this.localStream = stream;
						this.$refs.localVideo.srcObject = stream;
						resolve(stream);
					}).catch((e) => {
						this.$message.error("打开麦克风失败")
						console.log("打开麦克风失败:" + e.message)
						reject(e);
					})
				}
			})
		},
		startChatTime() {
			this.videoTime = 0;
			this.videoTimer && clearInterval(this.videoTimer);
			this.videoTimer = setInterval(() => {
				this.videoTime++;
			}, 1000)
		},
		checkDevEnable() {
			// 检测摄像头
			if (!this.camera.isEnable()) {
				this.message.error("访问摄像头失败");
				return false;
			}
			// 检测webrtc
			if (!this.webrtc.isEnable()) {
				this.message.error("初始化RTC失败，原因可能是: 1.服务器缺少ssl证书 2.您的设备不支持WebRTC");
				return false;
			}
			return true;
		},
		startHeartBeat() {
			// 每15s推送一次心跳
			this.heartbeatTimer && clearInterval(this.heartbeatTimer);
			this.heartbeatTimer = setInterval(() => {
				this.API.heartbeat(this.friend.id);
			}, 15000)
		},
		close() {
			this.showRoom = false;
			this.camera.close();
			this.webrtc.close();
			this.audio.pause();
			this.videoTime = 0;
			this.videoTimer && clearInterval(this.videoTimer);
			this.heartbeatTimer && clearInterval(this.heartbeatTimer);
			this.videoTimer = null;
			this.heartbeatTimer = null;
			this.state = 'CLOSE';
			this.candidates = [];
		},
		onQuit() {
			if (this.isChating) {
				this.onHandup()
			} else if (this.isWaiting) {
				this.onCancel();
			} else {
				this.close();
			}
		}
	},
	computed: {
		width() {
			return this.isVideo ? '960px' : '360px'
		},
		title() {
			let strTitle = `${this.modeText}通话-${this.friend.nickName}`;
			if (this.isChating) {
				strTitle += `(${this.currentTime})`;
			} else if (this.isWaiting) {
				strTitle += `(呼叫中)`;
			}
			return strTitle;
		},
		currentTime() {
			let min = Math.floor(this.videoTime / 60);
			let sec = this.videoTime % 60;
			let strTime = min < 10 ? "0" : "";
			strTime += min;
			strTime += ":"
			strTime += sec < 10 ? "0" : "";
			strTime += sec;
			return strTime;
		},
		configuration() {
			const iceServers = this.$store.state.configStore.webrtc.iceServers;
			return {
				iceServers: iceServers
			}
		},
		isVideo() {
			return this.mode == "video"
		},
		modeText() {
			return this.isVideo ? "视频" : "语音";
		},
		isChating() {
			return this.state == "CHATING";
		},
		isWaiting() {
			return this.state == "WAITING";
		},
		isClose() {
			return this.state == "CLOSE";
		}
	},
	mounted() {
		// 初始化音频文件
		this.initAudio();
	},
	created() {
		// 监听页面刷新事件
		window.addEventListener('beforeunload', () => {
			this.onQuit();
		});
	},
	beforeUnmount() {
		this.onQuit();
	}
}
</script>

<style lang="scss">
.rtc-private-video {
	position: relative;

	.el-loading-text {
		color: white !important;
		font-size: 16px !important;
	}

	.path {
		stroke: white !important;
	}

	.rtc-video-box {
		position: relative;
		background-color: #eeeeee;

		.rtc-video-friend {
			height: 70vh;

			.friend-head-image {
				position: absolute;
			}

			video {
				width: 100%;
				height: 100%;
				object-fit: cover;
				transform: rotateY(180deg);
			}
		}

		.rtc-video-mine {
			position: absolute;
			z-index: 99999;
			width: 25vh;
			right: 0;
			bottom: -1px;

			video {
				width: 100%;
				object-fit: cover;
				transform: rotateY(180deg);
			}
		}
	}

	.rtc-voice-box {
		position: relative;
		display: flex;
		justify-content: center;
		align-items: center;
		width: 100%;
		height: 300px;
		background-color: var(--im-color-primary-light-9);

		.rtc-voice-name {
			text-align: center;
			font-size: 20px;
			font-weight: 600;
		}
	}

	.rtc-control-bar {
		display: flex;
		justify-content: space-around;
		padding: 10px;

		.icon {
			font-size: 50px;
			cursor: pointer;
		}
	}
}
</style>
